﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using Microsoft.Practices.CompositeUI;
using Microsoft.Practices.CompositeUI.SmartParts;
using Microsoft.Practices.ObjectBuilder;
using WPFCABShell.Shell.Interface.Diagnostics;
using WPFCABShell.Shell.Interface.Services.Settings;

namespace WPFCABShell.Shell.Visualizers
{
	public partial class LogVisualizer : IBuilderAware
	{
		private readonly IVisualizer _visualizer;
		private readonly IList<LogLevel> _allLogLevels;
		private readonly TextBoxTraceListener _listener;
		private IDictionary<string, LogLevel> _logLevelForLogNames;
		private const LogLevel _defaultLogLevel = LogLevel.Error;
		private static readonly string _settingsKey = typeof(LogVisualizer).FullName;
		private const string _logLevelsSettingsKey = "LogLevels";

		public static readonly DependencyProperty AutoScrollProperty = DependencyProperty.Register("AutoScroll",
			typeof(bool),
			typeof(LogVisualizer),
			new FrameworkPropertyMetadata(true));

		public bool AutoScroll
		{
			get
			{
				return (bool)GetValue(AutoScrollProperty);
			}
			set
			{
				SetValue(AutoScrollProperty, value);
			}
		}

		private ISettingsService SettingsService
		{
			get
			{
				return _visualizer.CabRootWorkItem.Services.Get<ISettingsService>();
			}
		}

		public Log.LogCollection Logs
		{
			get
			{
				return Log.Logs;
			}
		}

		public IList<LogLevel> AllLogLevels
		{
			get
			{
				return _allLogLevels;
			}
		}

		public LogVisualizer()
		{
			_allLogLevels = new List<LogLevel>();

			foreach (LogLevel logLevel in Enum.GetValues(typeof(LogLevel)))
			{
				_allLogLevels.Add(logLevel);
			}

			ResourceDictionary resourceDictionary = new ResourceDictionary();
			resourceDictionary.Source = new Uri("pack://application:,,,/Shell;component/DefaultSkin.xaml");
			Resources.MergedDictionaries.Add(resourceDictionary);
			Resources["LogLevelConverter"] = new LogLevelConverter(this);
			InitializeComponent();
			_listener = new TextBoxTraceListener(this);
		}

		[InjectionConstructor]
		public LogVisualizer(WorkItem rootWorkItem, IVisualizer visualizer)
			: this()
		{
			_visualizer = visualizer;
			//this will show our visualizer in the main visualizer window
			rootWorkItem.Workspaces["MainWorkspace"].Show(this, new SmartPartInfo("Log Visualizer", string.Empty));
		}

		private void LoadLogLevels()
		{
			if (_logLevelForLogNames != null)
			{
				return;
			}

			ISettings settings = SettingsService.GetSettings(_settingsKey);

			if (settings.Contains(_logLevelsSettingsKey))
			{
				_logLevelForLogNames = settings.Get(_logLevelsSettingsKey) as IDictionary<string, LogLevel>;
			}

			if (_logLevelForLogNames == null)
			{
				_logLevelForLogNames = new Dictionary<string, LogLevel>();
			}
		}

		private void ConfigureLog(Log log, LogLevel logLevel)
		{
			Debug.Assert(log != null);

			if (!log.TraceSource.Listeners.Contains(_listener))
			{
				log.TraceSource.Listeners.Add(_listener);
			}

			if (logLevel == _defaultLogLevel)
			{
				_logLevelForLogNames.Remove(log.Name);
			}
			else
			{
				_logLevelForLogNames[log.Name] = logLevel;
			}

			log.TraceSource.Switch.Level = ConvertToSourceLevels(logLevel);
		}

		private static SourceLevels ConvertToSourceLevels(LogLevel logLevel)
		{
			switch (logLevel)
			{
				case LogLevel.Verbose:
					return SourceLevels.Verbose;
				case LogLevel.Information:
					return SourceLevels.Information;
				case LogLevel.Warning:
					return SourceLevels.Warning;
				case LogLevel.Error:
					return SourceLevels.Error;
				case LogLevel.Critical:
					return SourceLevels.Critical;
				default:
					throw new InvalidOperationException();
			}
		}

		private void _logLevelComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
		{
			Debug.Assert(sender is ComboBox);
			Debug.Assert((sender as ComboBox).DataContext is Log);
			ComboBox comboBox = sender as ComboBox;
			Log log = comboBox.DataContext as Log;
			ConfigureLog(log, (LogLevel)comboBox.SelectedItem);
		}

		private void _clearButton_Click(object sender, RoutedEventArgs e)
		{
			_outputTextBox.Clear();
		}

		void IBuilderAware.OnBuiltUp(string id)
		{
		}

		void IBuilderAware.OnTearingDown()
		{
			ISettings settings = SettingsService.GetSettings(_settingsKey);
			settings.Add(_logLevelsSettingsKey, _logLevelForLogNames);
			settings.Save();
		}

		private sealed class LogLevelConverter : IValueConverter
		{
			private readonly LogVisualizer _logVisualizer;

			public LogLevelConverter(LogVisualizer logVisualizer)
			{
				Debug.Assert(logVisualizer != null);
				_logVisualizer = logVisualizer;
			}

			public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
			{
				Debug.Assert(value is Log);

				//HACK: circumvents an awful life-cycle problem with CAB visualizers and services
				_logVisualizer.LoadLogLevels();
				Log log = value as Log;
				LogLevel logLevel = _defaultLogLevel;

				if (_logVisualizer._logLevelForLogNames.ContainsKey(log.Name))
				{
					logLevel = _logVisualizer._logLevelForLogNames[log.Name];
				}

				_logVisualizer.ConfigureLog(log, logLevel);
				return logLevel;
			}

			public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
			{
				throw new NotSupportedException();
			}
		}

		private sealed class TextBoxTraceListener : TraceListener
		{
			private readonly LogVisualizer _logVisualizer;

			private TextBox TextBox
			{
				get
				{
					return _logVisualizer._outputTextBox;
				}
			}

			public TextBoxTraceListener(LogVisualizer logVisualizer)
			{
				Debug.Assert(logVisualizer != null);
				_logVisualizer = logVisualizer;
			}

			private delegate void WriteHandler(string message);
			public override void Write(string message)
			{
				if (!TextBox.Dispatcher.CheckAccess())
				{
					TextBox.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Background, new WriteHandler(Write), message);
				}
				else
				{
					TextBox.AppendText(message);

					if (_logVisualizer.AutoScroll)
					{
						TextBox.ScrollToEnd();
					}
				}
			}

			private delegate void WriteLineHandler(string value);
			public override void WriteLine(string message)
			{
				if (!TextBox.Dispatcher.CheckAccess())
				{
					TextBox.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Background, new WriteLineHandler(WriteLine), message);
				}
				else
				{
					TextBox.AppendText(message);
					TextBox.AppendText(Environment.NewLine);

					if (_logVisualizer.AutoScroll)
					{
						TextBox.ScrollToEnd();
					}
				}
			}
		}
	}
}
